package modules

import (
	"fmt"
	"log"
	"strings"
	"time"

	"missevanbot/models"
	"missevanbot/modules/thirdparty"
	"missevanbot/utils"
)

const baseTime = 1642780800000 // 2022-01-22T00:00:00

// Checkin does the action of user checkin.
func Checkin(rid, uid int64, name string) (ok bool) {
	prefix := prefixRoom(rid)
	key := prefix + "checkin:" + utils.Itoa(uid)     // missevan:[RoomID]:checkin:[UID]
	old := rdb.SetBit(ctx, key, diffDays(), 1).Val() // old is the original value.
	if old == 1 {
		// Already checked in.
		return
	}

	// Pushes the UID and Username into the rank list.
	rdb.RPush(ctx, prefix+fmt.Sprintf("rank:%s:id", utils.Today()), uid)
	rdb.RPush(ctx, prefix+fmt.Sprintf("rank:%s:name", utils.Today()), name)

	poem, err := thirdparty.PoemText()
	if err != nil {
		poem = models.TplDefaultPoem
	}
	rdb.HSet(ctx, KeyLuckWords, utils.Itoa(uid), fmt.Sprintf("今日运势：%s\n\n%s", Word(WordLuck), poem)) // Stores the user luck word.
	return true
}

type CStats struct {
	Total  int64  // Total checkins number.
	Consec int64  // Consecutive checkins number.
	Luck   string // Daily luck word.
}

func CheckinStats(rid, uid int64) CStats {
	key := prefixRoom(rid) + "checkin:" + utils.Itoa(uid) // missevan:[RoomID]:checkin:[UID]

	total := rdb.BitCount(ctx, key, nil).Val()

	bs, err := rdb.Get(ctx, key).Bytes()
	if err != nil {
		log.Println("CCC" + err.Error())
	}

	luck := rdb.HGet(ctx, KeyLuckWords, utils.Itoa(uid)).Val()

	return CStats{
		Total:  total,
		Consec: countConsec(bs, diffDays()),
		Luck:   luck,
	}
}

func diffDays() int64 {
	diff := time.Now().Sub(time.UnixMilli(baseTime)).Hours() / 24
	return int64(diff)
}

// The countConsec counts the number of consecutive ones on the left of the idx position.
func countConsec(buf []byte, idx int64) (n int64) {
	dst := make([]int8, 0, len(buf)*8)
	for _, v := range buf {
		for i := 0; i < 8; i++ {
			move := uint(7 - i)
			dst = append(dst, int8((v>>move)&1))
		}
	}

	for i := idx; i >= 0; i-- {
		if dst[i] != 1 {
			if i < idx {
				return
			}
		} else {
			n++
		}
	}
	return
}

// CheckinRank returns the rank of checkin task today.
func CheckinRank(rid int64) string {
	prefix := prefixRoom(rid)
	key := prefix + fmt.Sprintf("rank:%s:id", utils.Today())
	nKey := prefix + fmt.Sprintf("rank:%s:name", utils.Today())

	listID := rdb.LRange(ctx, key, 0, -1)
	listName := rdb.LRange(ctx, nKey, 0, -1)
	result := strings.Builder{}
	result.WriteString("\n")
	for k, v := range listID.Val() {
		cs := CheckinStats(rid, utils.MustAtoi(v))
		result.WriteString(fmt.Sprintf("Rank %d. [%s] 连续签到%d天", k+1, listName.Val()[k], cs.Consec))
		if k < len(listID.Val())-1 {
			result.WriteString("\n")
		}
	}
	return result.String()
}
